﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Workflow_Core_API.Persisted;
using WorkflowCore.Interface;
using WorkflowCore.Models;
using WorkflowCore.Models.DefinitionStorage.v1;
using WorkflowCore.Persistence.EntityFramework.Services;
using WorkflowCore.Primitives;
using WorkflowCore.Service.Dto.Fundamentals;
using WorkflowCore.Service.Dto.Input;
using WorkflowCore.Service.Persisted;
using WorkflowCore.Service.Steps;
using WorkflowCore.Service.Strps;
using WorkflowCore.Services.DefinitionStorage;
using EndStep = WorkflowCore.Service.Strps.EndStep;

namespace WorkflowCore.Service.Manger
{
    public class WorkflowManger : IWorkflowManger
    {
        private readonly IDefinitionLoader definitionLoader;
        private readonly WorkflowCoreDbcontext dbcontext;

        public WorkflowManger(IDefinitionLoader definitionLoader, WorkflowCoreDbcontext dbcontext)
        {
            this.definitionLoader = definitionLoader;
            this.dbcontext = dbcontext;
        }

        /// <summary>
        /// 往WorkflowCore工作流系统中添加工作流模型
        /// </summary>
        /// <param name="definition"></param>
        /// //方法的目的是将一个持久化的工作流定义转换为一个特定的格式，并加载到流程引擎的内存中。
        public void AddDefinition(PersistedWorkflowDefinition definition)
        {
            //实例化一个工作流
            DefinitionSourceV1 source = new()
            {
                Id = definition.WorkflowDefinitionId,
                DataType = $"{typeof(Dictionary<string, object>).FullName},{typeof(Dictionary<string, object>).Assembly.FullName}",
                Version = definition.Version,
                Description = definition.Title
            };
            //将流程模版中的步骤反序列化为一个数组步骤类
            var steps = JsonConvert.DeserializeObject<List<WorkflowStepNodeInput>>(definition.StepNodes);
            //判断是否为空
            if (steps.Any())
            {
                source.Steps.Add(new()
                {
                    Id = nameof(StartStep),
                    NextStepId = steps.FirstOrDefault().StepId,
                    Name = nameof(StartStep),
                    StepType = $"{typeof(StartStep).FullName},{typeof(StartStep).Assembly.FullName}"
                });
                foreach (var step in steps)
                {
                    var foreachStep = new StepSourceV1
                    {
                        Id = step.StepId,
                        Name = step.StepName,
                        NextStepId = step.NextStepId
                    };
                    if (step.AuditType == StepBodyAuditTypeEnum.Person)
                    {
                        foreachStep.StepType = $"{typeof(PersonForeach).FullName},{typeof(PersonForeach).Assembly.FullName}";
                    }
                    else if (step.AuditType == StepBodyAuditTypeEnum.Role)
                    {
                        foreachStep.StepType = $"{typeof(RoleForeach).FullName},{typeof(RoleForeach).Assembly.FullName}";
                    }
                    var auditStep = new StepSourceV1
                    {
                        Id = $"{step.StepId}@{Guid.NewGuid()}",
                        Name = $"{step.StepName}@{Guid.NewGuid()}",
                        StepType = $"{typeof(AuditStep).FullName},{typeof(AuditStep).Assembly.FullName}"
                    };
                    if (step.Detail is not null)
                    {
                        if (step.Detail.AuditPersons.Any())
                        {
                            foreachStep.Inputs.TryAdd("Collection"
                                , $"\"{JsonConvert.SerializeObject(step.Detail.AuditPersons)}\"");
                        }
                    }

                    BuildWhen(auditStep, step);
                    foreachStep.Do.Add(new() { auditStep });
                    source.Steps.Add(foreachStep);
                }
                source.Steps.Add(new()
                {
                    Id = nameof(EndStep),
                    Name = nameof(EndStep),
                    StepType = $"{typeof(EndStep).FullName},{typeof(EndStep).Assembly.FullName}"
                });
            };
            var json = JsonConvert.SerializeObject(source);
            definitionLoader.LoadDefinition(json, Deserializers.Json);
        }
        /// <summary>
        /// 构造When及其子节点(也就是Agree与Reject这两个步骤)
        /// </summary>
        /// <param name="stepBody"></param>
        /// <param name="stepNode"></param>
        public void BuildWhen(StepSourceV1 stepBody, WorkflowStepNodeInput stepNode)
        {
            var agree = new StepSourceV1
            {
                Id = $"{stepNode.StepId}@agree",
                Name = $"{stepNode.StepName}@agree",
                StepType = $"{typeof(AgreeStep).FullName},{typeof(AgreeStep).Assembly.FullName}"
            };
            var whenAgree = new StepSourceV1
            {
                Id = $"{stepNode.StepId}@whenAgree",
                Name = $"{stepNode.StepName}@whenAgree",
                StepType = $"{typeof(When).FullName},{typeof(When).Assembly.FullName}"
            };
            whenAgree.Inputs.TryAdd("ExpectedOutcome", $"\"{OperateType.Approval.GetDescription()}\"");
            whenAgree.Do.Add(new() { agree });
            stepBody.Do.Add(new() { whenAgree });


            var reject = new StepSourceV1
            {
                Id = $"{stepNode.StepId}@reject",
                Name = $"{stepNode.StepName}@reject",
                StepType = $"{typeof(RejectStep).FullName},{typeof(RejectStep).Assembly.FullName}"
            };
            var whenReject = new StepSourceV1
            {
                Id = $"{stepNode.StepId}@whenReject",
                Name = $"{stepNode.StepName}@whenReject",
                StepType = $"{typeof(When).FullName},{typeof(When).Assembly.FullName}"
            };
            whenReject.Inputs.TryAdd("ExpectedOutcome", $"\"{OperateType.Sendback.GetDescription()}\"");
            whenReject.Do.Add(new() { reject });
            stepBody.Do.Add(new() { whenReject });
        }

        public void AddAudit(IStepExecutionContext context
            , OperateType operateType
            , WorkFlowAuditStatus status
            , DateTime? auditTime
            , long? auditUserId
            , string? auditUserName)
        {
            dbcontext.persistedWorkflowAuditor.Add(new()
            {
                WorkflowId = context.Workflow.Id,
                WorkflowDefinitionId = context.Workflow.WorkflowDefinitionId,
                ExecutionPointerId = new Guid(context.ExecutionPointer.Id),
                OperateType = operateType,
                Status = status,
                StepId = context.Step.ExternalId.Split('@')[0],
                StepName = context.Step.Name,
                AuditTime = auditTime,
                CreatedTime = DateTime.Now,
                UserId = auditUserId,
                UserName = auditUserName
            });
            dbcontext.SaveChanges();
        }
    }
}
